#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/resource.h>
#include <locale.h>
#include <sbase.h>
#include <ibase.h>
#include <arpa/inet.h>
#include "stime.h"
#include "base64.h"
#include "base64indexdhtml.h"
#include "http.h"
#include "iniparser.h"
#include "logger.h"
#include "timer.h"
#include "trie.h"
#ifndef HTTP_BUF_SIZE
#define HTTP_BUF_SIZE           131072
#endif
#ifndef HTTP_LINE_SIZE
#define HTTP_LINE_SIZE          4096
#endif
#ifndef HTTP_QUERY_MAX
#define HTTP_QUERY_MAX          1024
#endif
#define HTTP_LINE_MAX           65536
#define HTTP_RESP_OK            "HTTP/1.0 200 OK\r\n\r\n"
#define HTTP_BAD_REQUEST        "HTTP/1.0 400 Bad Request\r\n\r\n"
#define HTTP_NOT_FOUND          "HTTP/1.0 404 Not Found\r\n\r\n" 
#define HTTP_NOT_MODIFIED       "HTTP/1.0 304 Not Modified\r\n\r\n"
#define HTTP_NO_CONTENT         "HTTP/1.0 206 No Content\r\n\r\n"
#ifndef LL
#define LL(x) ((long long int)x)
#endif
static char *http_default_charset = "UTF-8";
static char *httpd_home = NULL;
static int is_inside_html = 1;
static unsigned char *httpd_index_html_code = NULL;
static int  nhttpd_index_html_code = 0;
static SBASE *sbase = NULL;
static IBASE *ibase = NULL;
static SERVICE *httpd = NULL;
static SERVICE *indexd = NULL;
static SERVICE *queryd = NULL;
static dictionary *dict = NULL;
static void *http_headers_map = NULL;
static void *argvmap = NULL;
static void *logger = NULL;
//static int httpd_page_num  = 20;
//static int httpd_page_max  = 50;
static char *highlight_start = "<font color=red>";
static char *highlight_end = "</font>";
#define OP_CLEAR_CACHE      0x01
static char *e_argvs[] =
{
    "query",
#define E_ARGV_QUERY        0
    "fieldsfilter",
#define E_ARGV_FIELDFILTER  1
    "orderby",
#define E_ARGV_ORDERBY      2
    "from",
#define E_ARGV_FROM         3
    "display",
#define E_ARGV_DISPLAY      4
    "order",
#define E_ARGV_ORDER        5
    "catgroup",
#define E_ARGV_CATGROUP     6
    "catfilter",
#define E_ARGV_CATFILTER    7
    "rangefilter",
#define E_ARGV_RANGEFILTER  8
    "phrase",
#define E_ARGV_PHRASE       9
    "count",
#define E_ARGV_COUNT        10
    "not",
#define E_ARGV_NOT          11
    "rank",
#define E_ARGV_RANK         12
    "slevel",
#define E_ARGV_SLEVEL       13
    "xup",
#define E_ARGV_XUP          14
    "xdown",
#define E_ARGV_XDOWN        15
    "qfunc",
#define E_ARGV_QFUNC        16
    "qfhits",
#define E_ARGV_QFHITS       17
    "rel",
#define E_ARGV_REL          18
    "hitscale",
#define E_ARGV_HITSCALE     19
    "op",            
#define E_ARGV_OP           20
    "qid",
#define E_ARGV_QID          21
    "booland",
#define E_ARGV_BOOLAND      22
    "bhits",
#define E_ARGV_BHITS        23
    "bfhits",
#define E_ARGV_BFHITS       24
    "bphrase",
#define E_ARGV_BPHRASE      25
    "bnterm",
#define E_ARGV_BNTERM       26
    "bxcatup",
#define E_ARGV_BXCATUP      27
    "bxcatdown",
#define E_ARGV_BXCATDOWN    28
    "brank"
#define E_ARGV_BRANK        29
};
#define  E_ARGV_NUM         30
int httpd_request_handler(HTTP_REQ *httpRQ, IQUERY *query);
/* packet reader for indexd */
int indexd_packet_reader(CONN *conn, CB_DATA *buffer)
{
    return -1;
}

/* indexd packet handler */
int indexd_packet_handler(CONN *conn, CB_DATA *packet)
{
    IHEAD *req = NULL;

    if(conn && packet && (req = (IHEAD *)packet->data)) 
    {
        if(req->length > 1024 * 1024 * 32)
        {
            fprintf(stdout, "%s::%d cmd:%d length:%d\n", __FILE__, __LINE__, req->cmd, req->length);
        }
        if(req->length > 0)
        {
            conn->save_cache(conn, req, packet->ndata);
            return conn->recv_chunk(conn, req->length);
        }
        else goto err_end;
    }
err_end:
    return conn->close(conn);
}

/* index  handler */
int indexd_index_handler(CONN *conn)
{
    CB_DATA *chunk = NULL, *cache = NULL;
    char *p = NULL, *end = NULL;
    IBDATA block = {0};
    IHEAD resp = {0};
    IHEAD *req = NULL;
    DOCHEADER *docheader = NULL;
    int i = 0, count = 0;

    if(conn)
    {
        chunk = (CB_DATA *)(conn->chunk);
        cache = (CB_DATA *)(conn->cache);
        if(cache && (req = (IHEAD *)(cache->data)) && chunk 
                && (p = chunk->data) && chunk->ndata > 0)
        {
            if(chunk->ndata < sizeof(int)) goto err_end; 
            end = p + chunk->ndata;
            memcpy(&count, p, sizeof(int));
            p += sizeof(int);
            while(p < end && i < count)
            {
                memcpy(&(block.ndata), p, sizeof(int));
                p += sizeof(int);
                block.data = p;
                docheader = (DOCHEADER *)p;
                p += block.ndata;
                if(block.ndata <= 0 || p > end || docheader->size != block.ndata)
                {
                    FATAL_LOGGER(logger, "Invalid packet:%d cmd:%d at documents[%d] count:%d chunk[%d] ndata:%d size:%d p:%p end:%p", req->id, req->cmd, i, count, chunk->ndata, block.ndata, docheader->size, p, end); 
                    goto err_end;
                }
                else
                {
                    if((ibase_add_document(ibase, &block)) != 0)
                    {
                        FATAL_LOGGER(logger, "Add documents[%d][%d] failed, %s", i, docheader->globalid, strerror(errno));
                        goto err_end;
                    }
                }
                ++i;
            }
            resp.id = req->id;
            resp.cid = req->cid;
            resp.nodeid = req->nodeid;
            resp.cmd = req->cmd + 1;
            resp.status = IB_STATUS_OK;
            resp.length = 0;
            conn->push_chunk(conn, &resp, sizeof(IHEAD));
            return conn->over_session(conn);
        }
err_end:
        resp.status = IB_STATUS_ERR;
        resp.length = 0;
        conn->push_chunk(conn, &resp, sizeof(IHEAD));
        return conn->over_session(conn);
    }
    return -1;
}

/* indexd data handler */
int indexd_data_handler(CONN *conn, CB_DATA *packet, CB_DATA *cache, CB_DATA *chunk)
{
    int ret = -1, n = 0, id = 0, *np = NULL, *enp = NULL, len = 0;
    char *p = NULL, *end = NULL, *summary = NULL;
    IHEAD resp = {0}, *presp = NULL;
    IRES *res = NULL, xres = {0};
    IRECORD *records = NULL;
    IQUERY *pquery = NULL;
    HTTP_REQ httpRQ = {0};
    CB_DATA *block = NULL;
    ICHUNK *ichunk = NULL;
    IQSET *qset = NULL;
    IHEAD *req = NULL;

    if(conn && packet && (req = (IHEAD *)cache->data))
    {
        if(chunk && chunk->data && chunk->ndata > 0)
        {
            //fprintf(stdout, "%s::%d req->id:%lld req->cmd:%d req->length:%d\n", __FILE__, __LINE__, LL(req->id), req->cmd, req->length);
            switch(req->cmd)
            {
                case IB_REQ_INDEX:
                    return indexd_index_handler(conn);
                    break;
                case IB_REQ_UPDATE:
                    return indexd_index_handler(conn);
                    break;
                case IB_REQ_QPARSE:
                    {
                        p = chunk->data;
                        end = p + chunk->ndata;
                        if(http_argv_parse(p, end, &httpRQ) == -1)goto err_end;
                        len = sizeof(IHEAD) + sizeof(IQUERY);
                        if((block = queryd->newchunk(queryd, len)))
                        {
                            pquery = (IQUERY *)(block->data + sizeof(IHEAD));
                            if(httpd_request_handler(&httpRQ, pquery) <= 0)
                            {
                                queryd->pushchunk(queryd, (struct _CHUNK *)block);
                                goto end;
                            }
                            else
                            {
                                presp = (IHEAD *)block->data;
                                presp->id = req->id;
                                presp->cid = req->cid;
                                presp->nodeid = req->nodeid;
                                presp->cmd = IB_RESP_QPARSE;
                                presp->status = IB_STATUS_OK;
                                presp->length = sizeof(IQUERY);
                                if(conn->send_chunk(conn, block, len) != 0)
                                    queryd->pushchunk(queryd, (struct _CHUNK *)block);
                            }
                            return 0;
                        }
                        else goto err_end;
                    }
                    break;
                case IB_REQ_QUERY:
                    {
                        if(chunk->ndata == sizeof(IQUERY))
                        {
                            pquery = (IQUERY *)chunk->data; 
                            if(pquery->qfunc == IB_QFUNC_XQUERY) ichunk = ibase_xquery(ibase, pquery);
                            else ichunk = ibase_bquery(ibase, pquery);
                            if(ichunk)
                            {
                                presp = &(ichunk->resp);
                                res = &(ichunk->res);
                                presp->id = req->id;
                                presp->cid = req->cid;
                                presp->nodeid = req->nodeid;
                                presp->cmd = IB_RESP_QUERY;
                                presp->status = IB_STATUS_OK;
                                presp->length = sizeof(IRES) + res->count * sizeof(IRECORD);
                                if(pquery->qid != req->id)
                                {
                                    FATAL_LOGGER(logger, "Invalid qid:%d to head->id:%d on remote[%s:%d] via %d", pquery->qid, req->id, conn->remote_ip, conn->remote_port, conn->fd);
                                    ibase_clean_cache(ibase);
                                    ret = -1;
                                }
                                else if(pquery->qid != res->qid)
                                {
                                    FATAL_LOGGER(logger, "Invalid qid:%d to res->qid:%d on remote[%s:%d] via %d", pquery->qid, res->qid, conn->remote_ip, conn->remote_port, conn->fd);
                                    ibase_clean_cache(ibase);
                                    ret = -1;
                                }
                                else ret = 0;
                                if(ret == 0)
                                {
                                    n = sizeof(IHEAD) + presp->length;
                                    conn->push_chunk(conn, (char *)ichunk, n);
                                    ibase_push_chunk(ibase, ichunk);
                                    return 0;
                                }
                                else resp.status = IB_STATUS_ERR;
                            }
                            else resp.status = IB_STATUS_OK;
                        }
                        else resp.status = IB_STATUS_ERR;
                        resp.id = req->id;
                        resp.cid = req->cid;
                        resp.nodeid = req->nodeid;
                        resp.cmd = req->cmd + 1;
                        resp.length = sizeof(IRES);
                        xres.qid = pquery->qid;
                        conn->push_chunk(conn, &resp, sizeof(IHEAD));
                        conn->push_chunk(conn, &xres, sizeof(IRES));
                        return conn->over_session(conn);
                    }
                    break;
                case IB_REQ_SUMMARY:
                    {
                        if(chunk->ndata >= sizeof(IQSET) && (qset = (IQSET *)chunk->data) 
                                && (chunk->ndata >= (qset->count * sizeof(IRECORD) + sizeof(IQSET))))
                        {
                            records = (IRECORD *)(chunk->data + sizeof(IQSET));
                            res = &(qset->res);
                            len = sizeof(IHEAD) + IB_SUMMARY_MAX * qset->count;
                            if((block = queryd->newchunk(queryd, len)))
                            {
                                summary = block->data + sizeof(IHEAD);
                                if((n = ibase_read_summary(ibase, qset, records, summary, 
                                                highlight_start, highlight_end)) > 0)
                                {
                                    presp = (IHEAD *)block->data;
                                    presp->id = req->id;
                                    presp->cid = req->cid;
                                    presp->nodeid = req->nodeid;
                                    presp->cmd = IB_RESP_SUMMARY;
                                    presp->status = IB_STATUS_OK;
                                    presp->length = n;
                                    if(conn->send_chunk(conn, block, n+sizeof(IHEAD)) != 0)
                                        queryd->pushchunk(queryd, (struct _CHUNK *)block);
                                }
                                else
                                {
                                    queryd->pushchunk(queryd, (struct _CHUNK *)block);
                                    goto err_end;
                                }
                                return 0;
                            }
                        }
                    }
                    break;
                case IB_REQ_ENABLE_TERM:
                    {
                        np = (int *)chunk->data;
                        enp = (int *)(chunk->data + chunk->ndata);
                        while(np < enp)
                        {
                            id = *np;
                            ibase_enable_term(ibase, id);
                            ++np;
                        }
                        goto end;
                    }
                    break;
                case IB_REQ_DISABLE_TERM:
                    {
                        np = (int *)chunk->data;
                        enp = (int *)(chunk->data + chunk->ndata);
                        while(np < enp)
                        {
                            id = *np;
                            ibase_disable_term(ibase, id);
                            ++np;
                        }
                        goto end;
                    }
                    break;
                case IB_REQ_ENABLE_DOC:
                    {
                        np = (int *)chunk->data;
                        enp = (int *)(chunk->data + chunk->ndata);
                        while(np < enp)
                        {
                            id = *np;
                            ibase_enable_document(ibase, id);
                            ++np;
                        }
                        goto end;
                    }
                    break;
                case IB_REQ_DISABLE_DOC:
                    {
                        np = (int *)chunk->data;
                        enp = (int *)(chunk->data + chunk->ndata);
                        while(np < enp)
                        {
                            id = *np;
                            ibase_disable_term(ibase, id);
                            ++np;
                        }
                        goto end;
                    }
                    break;
                default:
                    break;
            }

end:
            resp.id = req->id;
            resp.cid = req->cid;
            resp.nodeid = req->nodeid;
            resp.cmd = req->cmd + 1;
            resp.status = IB_STATUS_OK;
            resp.length = 0;
            conn->push_chunk(conn, &resp, sizeof(IHEAD));
            return conn->over_session(conn);
        }
err_end:
        resp.id = req->id;
        resp.cid = req->cid;
        resp.nodeid = req->nodeid;
        resp.status = IB_STATUS_ERR;
        resp.cmd = req->cmd + 1;
        resp.length = 0;
        return conn->push_chunk(conn, &resp, sizeof(IHEAD));
    }
    return -1;
}

/* indexd error handler */
int indexd_error_handler(CONN *conn, CB_DATA *packet, CB_DATA *cache, CB_DATA *chunk)
{
    if(conn)
    {
	    //fprintf(stdout, "%s::%d ERROR\n", __FILE__, __LINE__);
        conn->over_cstate(conn);
        return conn->over(conn);
    }
    return -1;
}

/* indexd timeout handler */
int indexd_timeout_handler(CONN *conn, CB_DATA *packet, CB_DATA *cache, CB_DATA *chunk)
{
    if(conn)
    {
	    //fprintf(stdout, "%s::%d TIMEOUT\n", __FILE__, __LINE__);
        conn->over_cstate(conn);
        return conn->over(conn);
    }
    return -1;
}

/* indexd transaction handler */
int indexd_trans_handler(CONN *conn, int tid)
{
    if(conn && tid >= 0)
    {
        return 0;
    }
    return -1;
}


/* httpd packet reader */
int httpd_packet_reader(CONN *conn, CB_DATA *buffer)
{
    if(conn)
    {
        return 0;
    }
    return -1;
}

/* converts hex char (0-9, A-Z, a-z) to decimal */
char hex2int(unsigned char hex)
{
    hex = hex - '0';
    if (hex > 9) {
        hex = (hex + '0' - 1) | 0x20;
        hex = hex - 'a' + 11;
    }
    if (hex > 15)
        hex = 0xFF;

    return hex;
}

#define URLDECODE(p, end, ps, high, low)                                                \
do                                                                                      \
{                                                                                       \
    while(*p != '\0' && *p != '&')                                                      \
    {                                                                                   \
        if(ps >= end) break;                                                            \
        else if(*p == '+') {*ps++ = 0x20;++p;continue;}                                 \
        else if(*p == '%')                                                              \
        {                                                                               \
            high = hex2int(*(p + 1));                                                   \
            if (high != 0xFF)                                                           \
            {                                                                           \
                low = hex2int(*(p + 2));                                                \
                if (low != 0xFF)                                                        \
                {                                                                       \
                    high = (high << 4) | low;                                           \
                    if (high < 32 || high == 127) high = '_';                           \
                    *ps++ = high;                                                       \
                    p += 3;                                                             \
                    continue;                                                           \
                }                                                                       \
            }                                                                           \
        }                                                                               \
        *ps++ = *p++;                                                                   \
    }                                                                                   \
    *ps = '\0';                                                                         \
}while(0)

/* request handler */
int httpd_request_handler(HTTP_REQ *httpRQ, IQUERY *query)
{
    char *p = NULL, *query_str = NULL, *not_str = "", *display = NULL, *range = NULL,
         *range_from = NULL, *range_to = NULL, *hitscale = NULL;
    int ret = -1, n = 0, i = 0, id = 0, phrase = 0, booland = 0, fieldsfilter = -1, catfilter = -1,
        slevel_filter, orderby = 0, order = 0, rangefilter = -1, int_index_from = 0, 
        catgroup = 0, int_index_to = 0, double_index_from = 0, op = 0, 
        double_index_to = 0, need_rank = 0;
    void *dp = NULL;

    if(httpRQ && query)
    {
        //p = chunk->data;
        //end = chunk->data + chunk->ndata;
        //if(http_argv_parse(p, end, &httpRQ) == -1)goto err_end;
        //fprintf(stdout, "%s::%d OK\n", __FILE__, __LINE__);
        for(i = 0; i < httpRQ->nargvs; i++)
        {
            if(httpRQ->argvs[i].nk > 0 && (n = httpRQ->argvs[i].k) > 0
                    && (p = (httpRQ->line + n)))
            {
                TRIETAB_GET(argvmap, p, httpRQ->argvs[i].nk, dp);
                if((id = ((long)dp - 1)) >= 0 && httpRQ->argvs[i].nv > 0
                        && (n = httpRQ->argvs[i].v) > 0
                        && (p = (httpRQ->line + n)))
                {
                    switch(id)
                    {
                        case E_ARGV_QUERY :
                            query_str = p;
                            break;
                        case E_ARGV_FIELDFILTER :
                            fieldsfilter = atoi(p);
                            break;
                        case E_ARGV_CATFILTER:
                            catfilter = atoi(p);
                            break;
                        case E_ARGV_ORDERBY:
                            orderby = atoi(p);
                            break;
                        case E_ARGV_FROM :
                            query->from = atoi(p);
                            break;
                        case E_ARGV_COUNT:
                            query->count = atoi(p);
                            break;
                        case E_ARGV_ORDER:
                            order = atoi(p);
                            break;
                        case E_ARGV_CATGROUP:
                            catgroup = atoi(p);
                            break;
                        case E_ARGV_DISPLAY:
                            display = p;
                            break;
                        case E_ARGV_RANGEFILTER:
                            range = p;
                            break;
                        case E_ARGV_PHRASE:
                            phrase = atoi(p);
                            break;
                        case E_ARGV_NOT:
                            not_str = p;
                            break;
                        case E_ARGV_RANK:
                            need_rank = atoi(p);
                            break;
                        case E_ARGV_SLEVEL:
                            slevel_filter = atoi(p);
                            break;
                        case E_ARGV_XUP:
                            query->bitxcat_up = atoi(p);
                            break;
                        case E_ARGV_XDOWN:
                            query->bitxcat_down = atoi(p);
                            break;
                        case E_ARGV_QFUNC:
                            query->qfunc = atoi(p);
                            break;
                        case E_ARGV_QFHITS:
                            query->qfhits = atoi(p);
                            break;
                        case E_ARGV_REL:
                            query->flag |= IB_QUERY_RELEVANCE;
                            break;
                        case E_ARGV_HITSCALE:
                            hitscale = p;
                            break;
                        case E_ARGV_OP:
                            op = atoi(p);
                            break;
                        case E_ARGV_BOOLAND:
                            booland = atoi(p);
                            break;
                        case E_ARGV_BHITS:
                            query->base_hits = atoi(p);
                            break;
                        case E_ARGV_BFHITS:
                            query->base_fhits = atoi(p);
                            break;
                        case E_ARGV_BPHRASE:
                            query->base_phrase = atoi(p);
                            break;
                        case E_ARGV_BNTERM:
                            query->base_nterm = atoi(p);
                            break;
                        case E_ARGV_BXCATUP:
                            query->base_xcatup = atoi(p);
                            break;
                        case E_ARGV_BXCATDOWN:
                            query->base_xcatdown = atoi(p);
                            break;
                        case E_ARGV_BRANK:
                            query->base_rank = atoi(p);
                            break;
                        default :
                            break;
                    }
                }
            }
        }
        /* clear cache */
        if(op == OP_CLEAR_CACHE) 
        {
            query->flag |= IB_CLEAR_CACHE;
            if(query_str == NULL) return op;
        }
        /* check query_str */
        if(query_str == NULL) return -1;
        //fprintf(stdout, "query:%s not:%s from:%d count:%d fieldsfilter:%d catfilter:%d orderby:%d order:%d\n", query_str, not_str, query->from, query->count, fieldsfilter, catfilter, orderby, order);
        REALLOG(logger, "query:%s not:%s from:%d count:%d fieldsfilter:%d catfilter:%d orderby:%d order:%d qfhits:%d base_hits:%d base_fhits:%d base_phrase:%d base_nterm:%d base_xcatup:%d base_xcatdown:%d base_rank:%d", query_str, not_str, query->from, query->count, fieldsfilter, catfilter, orderby, order, query->qfhits, query->base_hits, query->base_fhits, query->base_phrase, query->base_nterm, query->base_xcatup, query->base_xcatdown, query->base_rank);
        //query->ntop = query->from + query->count;
        query->ntop = IB_TOPK_NUM;
        //httpd_page_max * httpd_page_num;
        /* filter */
        if(fieldsfilter > 0)
            query->fields_filter = fieldsfilter;
        if(catfilter > 0)
            query->category_filter = catfilter;
        if(slevel_filter)
            query->slevel_filter = slevel_filter;
        /* display */
        if((p = display))
        {
            while(*p != '\0')
            {
                if(*p == 0x20 || *p == ',' || *p == ';')++p;
                if(*p != '\0') i = atoi(p);
                else break;
                while(*p >= '0' && *p <= '9')++p;
                if(i >= 0 && i  < IB_FIELDS_MAX)
                {
                    query->display[i].flag |= IB_IS_DISPLAY;
                    if(*p == '#' || *p =='|' || *p == '$' || *p == '@' || *p == '}')
                    {
                        query->display[i].flag |= IB_IS_HIGHLIGHT;
                        ++p;
                    }
                }
            }
        }
        /* group */
        if(catgroup) query->flag |= IB_QUERY_CATGROUP;
        /* rank */
        if(need_rank) query->flag |= IB_QUERY_RANK;
        /* order/order by/range */
        if(order < 0) query->flag |= IB_QUERY_RSORT;
        else query->flag |= IB_QUERY_SORT;
        int_index_from = ibase->state->int_index_from;
        int_index_to = int_index_from + ibase->state->int_index_fields_num;
        double_index_from = ibase->state->double_index_from;
        double_index_to = double_index_from + ibase->state->double_index_fields_num;
        if(orderby >= int_index_from && orderby < int_index_to)
        {
            query->int_order_field = orderby;
        }
        else if(orderby >= double_index_from && orderby < double_index_to)
        {
            query->double_order_field = orderby;
        }
        /* range */
        if(range)
        {
            p = range;
            while(*p == 0x20)++p;
            if(*p != '\0')rangefilter = atoi(p);
            while(*p != '[' && *p != '\0')++p;
            ++p;
            while(*p == 0x20)++p;
            if(*p >= 0 && *p <= '9') range_from = p;
            while((*p >= '0' && *p <= '9') || *p == '.')++p;
            if(*p == ',' || *p == ';')++p;
            while(*p == 0x20)++p;
            if(*p >= 0 && *p <= '9') range_to = p;
            if(rangefilter >= int_index_from && rangefilter < int_index_to)
            {
                query->int_range_field = rangefilter;
                if(range_from) query->int_range_from = atoi(range_from);
                if(range_to) query->int_range_to = atoi(range_to);
            }
            else if(rangefilter >= double_index_from && rangefilter < double_index_to)
            {
                query->double_range_field = rangefilter;
                if(range_from) query->double_range_from = atof(range_from);
                if(range_to) query->double_range_to = atof(range_to);
            }
        }
        /* hitscale */
        if((p = hitscale))
        {
           i = 0;
           while(*p != '\0') 
           {
               while(*p == 0x20 || *p == '\t' || *p == ',' || *p == ';')++p;
               query->hitscale[i] = atoi(p);
               while(*p >= '0' && *p <= '9')++p;
                ++i;
           }
        }
        if(phrase > 0) query->flag |= IB_QUERY_PHRASE;
        if(booland > 0) query->flag |= IB_QUERY_BOOLAND;
        //fprintf(stdout, "%s::%d query:%s OK\n", __FILE__, __LINE__, query_str);
        return (ret = ibase_qparser(ibase, query_str, not_str, query));
    }
    return ret;
}

/* httpd query handler */
int httpd_query_handler(CONN *conn, IQUERY *query)
{
    char buf[HTTP_BUF_SIZE], *summary = NULL, *p = NULL;
    int ret = -1, x = 0, n = 0;
    IRECORD *records = NULL;
    CB_DATA *block = NULL;
    ICHUNK *ichunk = NULL;
    IQSET qset = {0};
    IRES *res = NULL;

    if(conn)
    {
        //TIMER_INIT(timer);
        if(query && query->from < query->ntop && query->count > 0)
        {
            //fprintf(stdout, "%s::%d from:%d count:%d ntop:%d\n", __FILE__, __LINE__, query->from, query->count, query->ntop);
            //if(query->qfunc == IB_QFUNC_QUERY) ichunk = ibase_query(ibase, query);
            if(query->qfunc == IB_QFUNC_XQUERY) ichunk = ibase_xquery(ibase, query);
            else ichunk = ibase_bquery(ibase, query);
            if(ichunk)
            {
                res     = &(ichunk->res);
                records  = ichunk->records;
                qset.count = query->count;
                if((query->from + query->count) > res->count)
                    qset.count = res->count - query->from;
                x = query->from;
                /*
                   n = sprintf(line, "count:%d total:%d io_time:%u sort_time:%d time:%lld\n", 
                   res->count, res->total, res->io_time, res->sort_time, PT_LU_USEC(timer));
                   p = buf;
                   p += sprintf(p, "HTTP/1.0 200 OK\r\nContent-Type:text/html;charset=%s\r\n"
                   "Content-Length: %d\r\nConnection:Keep-Alive\r\n\r\n%s",
                   http_default_charset, n, line);
                   conn->push_chunk(conn, buf, (p - buf));
                   ret = 0;
                   */
                if(qset.count > 0 && (n = (IB_SUMMARY_MAX * qset.count)) > 0 
                        && (block = httpd->newchunk(httpd, n)))
                {
                    summary = block->data; 
                    qset.nqterms = query->nqterms;
                    memcpy(qset.qterms, query->qterms, query->nqterms * sizeof(QTERM));
                    memcpy(qset.displaylist, query->display, sizeof(IDISPLAY) * IB_FIELDS_MAX); 
                    memcpy(&(qset.res), res, sizeof(IRES));
                    records = &(ichunk->records[x]);
                    if((n = ibase_read_summary(ibase, &qset, records, summary, 
                                    highlight_start, highlight_end)) > 0)
                    {
                        //fprintf(stdout, "%s::%d ntop:%d from:%d count:%d n:%d summary:%s\n", __FILE__, __LINE__, query->ntop, query->from, query->count, n, summary);
                        p = buf;
                        p += sprintf(p, "HTTP/1.0 200 OK\r\nContent-Type:text/html;charset=%s\r\n"
                                "Content-Length: %d\r\nConnection:Keep-Alive\r\n\r\n", 
                                http_default_charset, n);
                        conn->push_chunk(conn, buf, (p - buf));
                        if((ret = conn->send_chunk(conn, block, n)) != 0)
                            httpd->pushchunk(httpd, (struct _CHUNK *)block);
                    }
                    else
                    {
                        httpd->pushchunk(httpd, (struct _CHUNK *)block);
                        ret = -1;
                    }
                }
                ibase_push_chunk(ibase, ichunk);
                if(ret == -1) goto err_end;
                return 0;
            }
        }
err_end:
        ret = conn->push_chunk(conn, HTTP_BAD_REQUEST, strlen(HTTP_BAD_REQUEST));
    }
    return ret;
}

/* packet handler */
int httpd_packet_handler(CONN *conn, CB_DATA *packet)
{
    char buf[HTTP_BUF_SIZE], file[HTTP_PATH_MAX], *p = NULL, *end = NULL;
    HTTP_REQ http_req = {0};
    int ret = -1, n = 0;
    struct stat st = {0};
    IQUERY query = {0};
    
    if(conn)
    {
        //TIMER_INIT(timer);
        p = packet->data;
        end = packet->data + packet->ndata;
        if(http_request_parse(p, end, &http_req, http_headers_map) == -1) goto err_end;
        if(http_req.reqid == HTTP_GET)
        {
            if(http_req.nargvs > 0 && httpd_request_handler(&http_req, &query) > 0) 
            {
                return httpd_query_handler(conn, &query);
            }
            else
            {
                if(is_inside_html && httpd_index_html_code && nhttpd_index_html_code > 0)
                {
                    p = buf;
                    p += sprintf(p, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
                            "Content-Type: text/html;charset=%s\r\n",
                            nhttpd_index_html_code, http_default_charset);
                    if((n = http_req.headers[HEAD_GEN_CONNECTION]) > 0)
                        p += sprintf(p, "Connection: %s\r\n", (http_req.hlines + n));
                    p += sprintf(p, "Connection:Keep-Alive\r\n");
                    p += sprintf(p, "\r\n");
                    conn->push_chunk(conn, buf, (p - buf));
                    return conn->push_chunk(conn, httpd_index_html_code, nhttpd_index_html_code);
                }
                else if(httpd_home)
                {
                    p = file;
                    if(http_req.path[0] != '/')
                        p += sprintf(p, "%s/%s", httpd_home, http_req.path);
                    else
                        p += sprintf(p, "%s%s", httpd_home, http_req.path);
                    if(http_req.path[1] == '\0')
                        p += sprintf(p, "%s", "index.html");
                    if((n = (p - file)) > 0 && lstat(file, &st) == 0)
                    {
                        if(st.st_size == 0)
                        {
                            return conn->push_chunk(conn, HTTP_NO_CONTENT, strlen(HTTP_NO_CONTENT));
                        }
                        else if((n = http_req.headers[HEAD_REQ_IF_MODIFIED_SINCE]) > 0
                                && str2time(http_req.hlines + n) == st.st_mtime)
                        {
                            return conn->push_chunk(conn, HTTP_NOT_MODIFIED, strlen(HTTP_NOT_MODIFIED));
                        }
                        else
                        {
                            p = buf;
                            p += sprintf(p, "HTTP/1.0 200 OK\r\nContent-Length:%lld\r\n"
                                    "Content-Type: text/html;charset=%s\r\n",
                                    (long long int)(st.st_size), http_default_charset);
                            if((n = http_req.headers[HEAD_GEN_CONNECTION]) > 0)
                                p += sprintf(p, "Connection: %s\r\n", http_req.hlines + n);
                            p += sprintf(p, "Last-Modified:");
                            p += GMTstrdate(st.st_mtime, p);
                            p += sprintf(p, "%s", "\r\n");//date end
                            p += sprintf(p, "%s", "\r\n");
                            conn->push_chunk(conn, buf, (p - buf));
                            return conn->push_file(conn, file, 0, st.st_size);
                        }
                    }
                    else
                    {
                        return conn->push_chunk(conn, HTTP_NOT_FOUND, strlen(HTTP_NOT_FOUND));
                    }
                }
                else
                {
                    return conn->push_chunk(conn, HTTP_BAD_REQUEST, strlen(HTTP_BAD_REQUEST));
                }
            }
        }
        else if(http_req.reqid == HTTP_POST)
        {
            if((n = http_req.headers[HEAD_ENT_CONTENT_LENGTH]) > 0
                    && (n = atol(http_req.hlines + n)) > 0)
            {
                conn->save_cache(conn, &http_req, sizeof(HTTP_REQ));
                return conn->recv_chunk(conn, n);
            }
        }
err_end:
        ret = conn->push_chunk(conn, HTTP_BAD_REQUEST, strlen(HTTP_BAD_REQUEST));
    }
    return ret;
}

/*  data handler */
int httpd_data_handler(CONN *conn, CB_DATA *packet, CB_DATA *cache, CB_DATA *chunk)
{
    char *p = NULL, *end = NULL, line[HTTP_LINE_SIZE]; 
    HTTP_REQ httpRQ = {0}, *http_req = NULL;
    int ret = -1, n = 0;
    IQUERY query = {0};

    if(conn && packet && cache && chunk && chunk->ndata > 0)
    {
        if((http_req = (HTTP_REQ *)cache->data))
        {
            if(http_req->reqid == HTTP_POST)
            {
                p = chunk->data;
                end = chunk->data + chunk->ndata;
                //fprintf(stdout, "%s::%d request:%s\n", __FILE__, __LINE__, p);
                if(http_argv_parse(p, end, &httpRQ) == -1)goto err_end;
                //fprintf(stdout, "%s::%d OK\n", __FILE__, __LINE__);
                if(httpd_request_handler(&httpRQ, &query) <= 0) goto err_end;
                //fprintf(stdout, "%s::%d OK\n", __FILE__, __LINE__);
                if(query.flag & IB_CLEAR_CACHE)
                {
                    ibase_clean_cache(ibase);
                    n = sprintf(line, "HTTP/1.0 200 OK\r\nContent-Type:text/html;charset=%s\r\n"
                                "Content-Length: 0\r\n\r\n", 
                                http_default_charset);
                    return conn->push_chunk(conn, line, n);
                }
                else
                {
                    /* query parser */
                    return httpd_query_handler(conn, &query);
                }
            }
        }
err_end: 
        ret = conn->push_chunk(conn, HTTP_BAD_REQUEST, strlen(HTTP_BAD_REQUEST));
    }
    return ret;
}

/* httpd timeout handler */
int httpd_timeout_handler(CONN *conn, CB_DATA *packet, CB_DATA *cache, CB_DATA *chunk)
{
    if(conn)
    {
        conn->over_cstate(conn);
        return conn->over(conn);
    }
    return -1;
}

/* OOB data handler for httpd */
int httpd_oob_handler(CONN *conn, CB_DATA *oob)
{
    if(conn)
    {
        return 0;
    }
    return -1;
}

/* Initialize from ini file */
int sbase_initialize(SBASE *sbase, char *conf)
{
    int i = 0, n = 0, used_for = -1, mmsource_status = 0, index_from = 0, index_count = 0;
    char *s = NULL, *p = NULL, *dir = NULL, *charset = NULL, line[HTTP_LINE_SIZE];
    void *dp = NULL;

    if((dict = iniparser_new(conf)) == NULL)
    {
        fprintf(stderr, "Initializing conf:%s failed, %s\n", conf, strerror(errno));
        _exit(-1);
    }
    /* http headers map */
    TRIETAB_INIT(http_headers_map);
    if(http_headers_map)
    {
        for(i = 0; i < HTTP_HEADER_NUM; i++)
        {
            p = http_headers[i].e;
            s = line;
            while(*p != '\0')
            {
                if(*p >= 'A' && *p <= 'Z')
                {
                    *s++ = *p++ - ('A' - 'a');
                }
                else *s++ = *p++;
            }
            *s = '\0';
            dp = (void *)((long)(i + 1));
            p = line;
            TRIETAB_ADD(http_headers_map, p, http_headers[i].elen, dp);
        }
    }
    TRIETAB_INIT(argvmap);
    if(argvmap == NULL) _exit(-1);
    else
    {
        for(i = 0; i < E_ARGV_NUM; i++)
        {
            dp = (void *)((long)(i+1));
            n = strlen(e_argvs[i]);
            TRIETAB_ADD(argvmap, e_argvs[i], n, dp);
        }
    }
    /* SBASE */
    sbase->nchilds = iniparser_getint(dict, "SBASE:nchilds", 0);
    sbase->connections_limit = iniparser_getint(dict, "SBASE:connections_limit", SB_CONN_MAX);
    setrlimiter("RLIMIT_NOFILE", RLIMIT_NOFILE, sbase->connections_limit);
    sbase->usec_sleep = iniparser_getint(dict, "SBASE:usec_sleep", SB_USEC_SLEEP);
    sbase->set_log(sbase, iniparser_getstr(dict, "SBASE:logfile"));
    sbase->set_evlog(sbase, iniparser_getstr(dict, "SBASE:evlogfile"));
    /* IBASE */
    used_for = iniparser_getint(dict, "IBASE:used_for", 0);
    mmsource_status = iniparser_getint(dict, "IBASE:mmsource_status", 1);
    if((dir = iniparser_getstr(dict, "IBASE:basedir")) == NULL || (ibase = ibase_init()) == NULL 
            || ibase->set_basedir(ibase, dir, used_for, mmsource_status) != 0)
    {
        fprintf(stderr, "Initialize ibase failed, %s", strerror(errno));
        _exit(-1);
    }
    if((p = iniparser_getstr(dict, "IBASE:dict_file")) 
            && (charset = iniparser_getstr(dict, "IBASE:dict_charset")))
    {
        ibase_set_dict(ibase, charset, p);
    }
    LOGGER_ROTATE_INIT(logger, iniparser_getstr(dict, "IBASE:query_log"), LOG_ROTATE_DAY);
    if((index_from = iniparser_getint(dict, "IBASE:int_index_from", 0)) >= 0
            && (index_count = iniparser_getint(dict, "IBASE:int_index_count", 0)) > 0)
    {
        ibase->set_int_index(ibase, dir, index_from, index_count);
    }
    if((index_from = iniparser_getint(dict, "IBASE:double_index_from", 0)) >= 0
            && (index_count = iniparser_getint(dict, "IBASE:double_index_count", 0)) > 0)
    {
        ibase->set_double_index(ibase, dir, index_from, index_count);
    }
    ibase->set_index_status(ibase, iniparser_getint(dict, "IBASE:index_status", 0));
    ibase->set_phrase_status(ibase, iniparser_getint(dict, "IBASE:phrase_status", 0));
    ibase->set_compression_status(ibase, iniparser_getint(dict, "IBASE:compression_status", 0));
    ibase->set_cache_status(ibase, iniparser_getint(dict, "IBASE:cache_status", 0));
    ibase->set_cache_life_time(ibase, iniparser_getint(dict, "IBASE:cache_life_time", 60));
    if((p = iniparser_getstr(dict, "IBASE:highlight_start"))) highlight_start = p;
    if((p = iniparser_getstr(dict, "IBASE:highlight_end"))) highlight_end = p;
    /* httpd */
    is_inside_html = iniparser_getint(dict, "HTTPD:is_inside_html", 1);
    httpd_home = iniparser_getstr(dict, "HTTPD:httpd_home");
    http_default_charset = iniparser_getstr(dict, "HTTPD:httpd_charset");
    /* decode html base64 */
    if(html_code_base64 && (n = strlen(html_code_base64)) > 0
            && (httpd_index_html_code = (unsigned char *)calloc(1, n + 1)))
    {
        nhttpd_index_html_code = base64_decode(httpd_index_html_code,
                (char *)html_code_base64, n);
    }
    if((httpd = service_init()) == NULL)
    {
        fprintf(stderr, "Initialize service failed, %s", strerror(errno));
        _exit(-1);
    }
    httpd->family = iniparser_getint(dict, "HTTPD:inet_family", AF_INET);
    httpd->sock_type = iniparser_getint(dict, "HTTPD:socket_type", SOCK_STREAM);
    httpd->ip = iniparser_getstr(dict, "HTTPD:service_ip");
    httpd->port = iniparser_getint(dict, "HTTPD:service_port", 80);
    httpd->working_mode = iniparser_getint(dict, "HTTPD:working_mode", WORKING_PROC);
    httpd->service_type = iniparser_getint(dict, "HTTPD:service_type", S_SERVICE);
    httpd->service_name = iniparser_getstr(dict, "HTTPD:service_name");
    httpd->nprocthreads = iniparser_getint(dict, "HTTPD:nprocthreads", 1);
    httpd->ndaemons = iniparser_getint(dict, "HTTPD:ndaemons", 0);
    httpd->use_iodaemon = iniparser_getint(dict, "HTTPD:use_iodaemon", 0);
    httpd->use_cond_wait = iniparser_getint(dict, "HTTPD:use_cond_wait", 0);
    httpd->set_log(httpd, iniparser_getstr(dict, "HTTPD:logfile"));
    httpd->session.packet_type = iniparser_getint(dict, "HTTPD:packet_type",PACKET_DELIMITER);
    httpd->session.packet_delimiter = iniparser_getstr(dict, "HTTPD:packet_delimiter");
    p = s = httpd->session.packet_delimiter;
    while(*p != 0 )
    {
        if(*p == '\\' && *(p+1) == 'n')
        {
            *s++ = '\n';
            p += 2;
        }
        else if (*p == '\\' && *(p+1) == 'r')
        {
            *s++ = '\r';
            p += 2;
        }
        else
            *s++ = *p++;
    }
    *s++ = 0;
    httpd->session.packet_delimiter_length = strlen(httpd->session.packet_delimiter);
    httpd->session.buffer_size = iniparser_getint(dict, "HTTPD:buffer_size", SB_BUF_SIZE);
    httpd->session.packet_reader = &httpd_packet_reader;
    httpd->session.packet_handler = &httpd_packet_handler;
    httpd->session.data_handler = &httpd_data_handler;
    httpd->session.timeout_handler = &httpd_timeout_handler;
    httpd->session.oob_handler = &httpd_oob_handler;
    /* QUERYD */
    if((queryd = service_init()) == NULL)
    {
        fprintf(stderr, "Initialize service failed, %s", strerror(errno));
        _exit(-1);
    }
    queryd->family = iniparser_getint(dict, "QUERYD:inet_family", AF_INET);
    queryd->sock_type = iniparser_getint(dict, "QUERYD:socket_type", SOCK_STREAM);
    queryd->ip = iniparser_getstr(dict, "QUERYD:service_ip");
    queryd->port = iniparser_getint(dict, "QUERYD:service_port", 3927);
    queryd->working_mode = iniparser_getint(dict, "QUERYD:working_mode", WORKING_PROC);
    queryd->service_type = iniparser_getint(dict, "QUERYD:service_type", S_SERVICE);
    queryd->service_name = iniparser_getstr(dict, "QUERYD:service_name");
    queryd->nprocthreads = iniparser_getint(dict, "QUERYD:nprocthreads", 1);
    queryd->ndaemons = iniparser_getint(dict, "QUERYD:ndaemons", 0);
    queryd->use_iodaemon = iniparser_getint(dict, "QUERYD:use_iodaemon", 0);
    queryd->use_cond_wait = iniparser_getint(dict, "QUERYD:use_cond_wait", 0);
    queryd->set_log(queryd, iniparser_getstr(dict, "QUERYD:logfile"));
    queryd->session.flag = O_NONBLOCK;
    queryd->session.packet_type = PACKET_CERTAIN_LENGTH;
    queryd->session.packet_length = sizeof(IHEAD);
    queryd->session.buffer_size = iniparser_getint(dict, "QUERYD:buffer_size", SB_BUF_SIZE);
    queryd->session.packet_handler = &indexd_packet_handler;
    queryd->session.data_handler = &indexd_data_handler;
    queryd->session.timeout_handler = &indexd_timeout_handler;

    /* INDEXD */
    if((indexd = service_init()) == NULL)
    {
        fprintf(stderr, "Initialize service failed, %s", strerror(errno));
        _exit(-1);
    }
    indexd->family = iniparser_getint(dict, "INDEXD:inet_family", AF_INET);
    indexd->sock_type = iniparser_getint(dict, "INDEXD:socket_type", SOCK_STREAM);
    indexd->ip = iniparser_getstr(dict, "INDEXD:service_ip");
    indexd->port = iniparser_getint(dict, "INDEXD:service_port", 4936);
    indexd->working_mode = iniparser_getint(dict, "INDEXD:working_mode", WORKING_PROC);
    indexd->service_type = iniparser_getint(dict, "INDEXD:service_type", S_SERVICE);
    indexd->service_name = iniparser_getstr(dict, "INDEXD:service_name");
    indexd->nprocthreads = iniparser_getint(dict, "INDEXD:nprocthreads", 1);
    indexd->ndaemons = iniparser_getint(dict, "INDEXD:ndaemons", 0);
    indexd->use_iodaemon = iniparser_getint(dict, "INDEXD:use_iodaemon", 0);
    indexd->use_cond_wait = iniparser_getint(dict, "INDEXD:use_cond_wait", 0);
    indexd->set_log(indexd, iniparser_getstr(dict, "INDEXD:logfile"));
    indexd->session.packet_type = PACKET_CERTAIN_LENGTH;
    indexd->session.packet_length = sizeof(IHEAD);
    indexd->session.buffer_size = iniparser_getint(dict, "INDEXD:buffer_size", SB_BUF_SIZE);
    indexd->session.packet_handler = &indexd_packet_handler;
    indexd->session.data_handler = &indexd_data_handler;
    indexd->session.timeout_handler = &indexd_timeout_handler;
        //return sbase->add_service(sbase, indexd);
    return (sbase->add_service(sbase, httpd) | sbase->add_service(sbase, queryd)
            | sbase->add_service(sbase, indexd));
}

static void indexd_stop(int sig)
{
    switch (sig)
    {
        case SIGINT:
        case SIGTERM:
            fprintf(stderr, "indexd server is interrupted by user.\n");
            if(sbase)sbase->stop(sbase);
            break;
        default:
            break;
    }
    return ;
}

int main(int argc, char **argv)
{
    char *conf = NULL, ch = 0;
    int is_daemon = 0;
    pid_t pid;

    /* get configure file */
    while((ch = getopt(argc, argv, "c:d")) != -1)
    {
        if(ch == 'c') conf = optarg;
        else if(ch == 'd') is_daemon = 1;
    }
    if(conf == NULL)
    {
        fprintf(stderr, "Usage:%s -d -c config_file\n", argv[0]);
        _exit(-1);
    }
    /* locale */
    setlocale(LC_ALL, "C");
    /* signal */
    signal(SIGTERM, &indexd_stop);
    signal(SIGINT,  &indexd_stop);
    signal(SIGHUP,  &indexd_stop);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
    if(is_daemon)
    {
        pid = fork();
        switch (pid) {
            case -1:
                perror("fork()");
                exit(EXIT_FAILURE);
                break;
            case 0: /* child process */
                if(setsid() == -1)
                    exit(EXIT_FAILURE);
                break;
            default:/* parent */
                _exit(EXIT_SUCCESS);
                break;
        }
    }
    /*setrlimiter("RLIMIT_NOFILE", RLIMIT_NOFILE, 65536)*/
    if((sbase = sbase_init()) == NULL)
    {
        exit(EXIT_FAILURE);
        return -1;
    }
    fprintf(stdout, "Initializing from configure file:%s\n", conf);
    /* Initialize sbase */
    if(sbase_initialize(sbase, conf) != 0 )
    {
        fprintf(stderr, "Initialize from configure file failed\n");
        return -1;
    }
    fprintf(stdout, "Initialized successed\n");
    sbase->running(sbase, 0);
    //sbase->running(sbase, 3600);
    //sbase->running(sbase, 60000000);
    //sbase->stop(sbase);
    ibase->clean(ibase);
    sbase->clean(&sbase);
    if(http_headers_map) TRIETAB_CLEAN(http_headers_map);
    if(argvmap){TRIETAB_CLEAN(argvmap);}
    if(dict)iniparser_free(dict);
    if(logger){LOGGER_CLEAN(logger);}
    if(httpd_index_html_code) free(httpd_index_html_code);
    return 0;
}
